﻿/**
 * DotNetFetion, a simple fetion library written by C#
 * Copyright (C) 2009  zucky
 * 
 * This file is part of DotNetFetion.
 * 
 * DotNetFetion is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * DotNetFetion is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with DotNetFetion.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * 本人实现的该类库是基本上参看php版本的fetion，具体是谁写的我在其源代码中没有注明，
 * 所以我也不知道是谁，在此表示感谢。飞信是中国移动的产品，归中国移动公司所有，
 * 请不要将该类库用于非法途径和商业途径，与此产生的一切法律后果与本人无关。
 * 
 * 有任何问题，可以访问我的博客：http://zucky.cnblogs.com 留言，或发email至ssdut@126.com，
 * 使用本类库请遵循GPL v3协议。
 **/

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Text.RegularExpressions;
using System.Xml;
using System.Security.Cryptography;

namespace com.dlut.ssdut.zucky.DotNetFetion
{
    public class Fetion
    {
        const string FSSISignInURL = "https://uid.fetion.com.cn/ssiportal/SSIAppSignIn.aspx";
        string phoneNo;
        string fetionNo;
        string pwd;
        WebClient client;
        string ssiappSignin;
        string sipcProxy;
        string urserId;
        SIPC sipc;
        string domain = "fetion.com.cn";
        string cnonce;
        string nonce;

        public Fetion(string phoneNo, string pwd)
        {
            this.phoneNo = phoneNo;
            this.pwd = pwd;
            Init();
        }

        private void Init()
        {
            ServicePointManager.CertificatePolicy = new TrustAllCertificatePolicy();

            client = new WebClient();
            string systemConfigXml = GetSystemConfig();
            ParaseConfig(systemConfigXml);
            //Console.WriteLine("飞信url为：" + ssiappSignin);
            //Console.WriteLine("登陆地址和端口号：" + sipcProxy);

            //Console.WriteLine("开始获取飞信号");
            // 获取飞信号
            string ssiAppSignInXml = SSIAppSignIn(ssiappSignin);
            //Console.WriteLine(ssiAppSignInXml);
            fetionNo = GetFetionNum(ssiAppSignInXml);
            //Console.WriteLine("飞信号是" + fetionNo);

            sipc = new SIPC(sipcProxy);
        }

        public void SendSMSToPhone(string phone, string smsText)
        {
            string request = string.Format("M {0} SIP-C/2.0\r\nF: {1}\r\nI: 2\r\nQ: 1 M\r\nT: tel:{2}\r\nN: SendSMS\r\nL: {3}\r\n\r\n", domain, fetionNo, phone, Encoding.UTF8.GetBytes(smsText).Length);
            request += smsText;
            string response = sipc.Request(request);
            //Console.WriteLine(response);
        }

        public void Logout()
        {
            string request = string.Format("R {0} SIP-C/2.0\r\nF: {1}\r\nI: 1 \r\nQ: 3 R\r\nX: 0\r\n\r\n", domain, fetionNo);
            string response = sipc.Request(request);
            //Console.WriteLine(response);
            sipc = null;
        }

        public void Login()
        {
            string loginXml = "<args><device type=\"PC\" version=\"6\" client-version=\"2.3.0230\" /><caps value=\"simple-im;im-session;temp-group\" /><events value=\"contact;permission;system-message\" /><user-info attributes=\"all\" /><presence><basic value=\"400\" desc=\"\" /></presence></args>";
            string loginRequest = string.Format("R {0} SIP-C/2.0\r\nF: {1}\r\nI: 1\r\nQ: 1 R\r\nL: {2}\r\n\r\n", domain, fetionNo, loginXml.Length);
            string login = loginRequest + loginXml;
            string serverResponse = sipc.Request(login);
            //Console.WriteLine("登陆第一步返回：");
            //Console.WriteLine(serverResponse);
            Regex regex = new Regex("nonce=\"(?<nonce>([\\S]+))\"");
            nonce = regex.Match(serverResponse).Result("${nonce}");
            //Console.WriteLine("noce为：" + nonce);

            loginXml = "<args><device type=\"PC\" version=\"6\" client-version=\"2.3.0230\" /><caps value=\"simple-im;im-session;temp-group\" /><events value=\"contact;permission;system-message\" /><user-info attributes=\"all\" /><presence><basic value=\"400\" desc=\"\" /></presence></args>";
            loginRequest = string.Format("R {0} SIP-C/2.0\r\nF: {1}\r\nI: 1\r\nQ: 2 R\r\nA: Digest response=\"{2}\",cnonce=\"{3}\"\r\nL: {4}\r\n\r\n", domain, fetionNo, ComputeNonceResponse(), cnonce, loginXml.Length);
            login = loginRequest + loginXml;
            serverResponse = sipc.Request(login);
            //Console.WriteLine("登录第二步返回：");
            //Console.WriteLine(serverResponse);
        }

        private string ComputeNonceResponse()
        {
            Random random = new Random(DateTime.Now.Millisecond);
            int rand = random.Next();
            cnonce = BinToHex(MD5(BitConverter.GetBytes(rand))).ToUpper();
            //Console.WriteLine("cnonce：" + cnonce);
            byte[] key = MD5(Encoding.UTF8.GetBytes(string.Format("{0}:{1}:{2}", fetionNo, domain, pwd)));
            string h1 = BinToHex(MD5(MergeBytes(key, Encoding.UTF8.GetBytes(string.Format(":{0}:{1}", nonce, cnonce))))).ToUpper();
            string h2 = BinToHex(MD5(Encoding.UTF8.GetBytes(string.Format("REGISTER:{0}", fetionNo)))).ToUpper();
            string response = BinToHex(MD5(Encoding.UTF8.GetBytes(string.Format("{0}:{1}:{2}", h1, nonce, h2))));
            return response;
        }

        private byte[] MergeBytes(byte[] bytes1, byte[] bytes2)
        {
            byte[] returnBytes = new byte[bytes1.Length + bytes2.Length];
            Array.Copy(bytes1, returnBytes, bytes1.Length);
            Array.Copy(bytes2, 0, returnBytes, bytes1.Length, bytes2.Length);
            return returnBytes;
        }

        private string GetFetionNum(string ssiAppSignInXml)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(ssiAppSignInXml);
            XmlElement userNode = (XmlElement)doc.GetElementsByTagName("user")[0];
            string uri = userNode.GetAttribute("uri");
            urserId = userNode.GetAttribute("user-id");
            Regex regex = new Regex("sip:([0-9]+)@fetion.com.cn");
            return regex.Match(uri).Value.Replace("sip:", "").Replace("@fetion.com.cn", "");
        }

        private string Post(string url, string postData)
        {
            return Encoding.UTF8.GetString(client.UploadData(url, "post", Encoding.UTF8.GetBytes(postData)));
        }

        private string Get(string url)
        {
            return Encoding.UTF8.GetString(client.DownloadData(url));
        }

        private string SSIAppSignIn(string url)
        {
            string par = string.Format("?mobileno={0}&pwd={1}", phoneNo, pwd);
            return Get(url + par);
        }

        /// <summary>
        /// 获取飞信的URL
        /// </summary>
        /// <returns></returns>
        private void ParaseConfig(string systemConfigXml)
        {
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(systemConfigXml);
            ssiappSignin = doc.GetElementsByTagName("ssi-app-sign-in")[0].InnerText;
            sipcProxy = doc.GetElementsByTagName("sipc-proxy")[0].InnerText;
        }

        private string GetSystemConfig()
        {
            //Console.WriteLine("Begin Getting System Config");
            string post = string.Format("<config><user mobile-no=\"{0}\" /><client type=\"PC\" version=\"2.3.0230\" platform=\"W5.1\" /><servers version=\"0\" /><service-no version=\"12\" /><parameters version=\"15\" /><hints version=\"13\" /><http-applications version=\"14\" /><client-config version=\"17\" /></config>", phoneNo);
            string url = "https://nav.fetion.com.cn/nav/getsystemconfig.aspx";
            string xml = Post(url, post);
            //Console.WriteLine(xml);
            //Console.WriteLine("Get System Config successfully");
            return xml;
        }

        private byte[] SHA1(byte[] toHash)
        {
            SHA1 sha = new SHA1CryptoServiceProvider();
            return sha.ComputeHash(toHash);
        }

        private byte[] MD5(byte[] toHash)
        {
            MD5 md5 = new MD5CryptoServiceProvider();
            return md5.ComputeHash(toHash);
        }

        private string BinToHex(byte[] binary)
        {
            string hex = BitConverter.ToString(binary);
            hex = hex.Replace("-", "").ToUpper();
            return hex;
        }
    }
}
